﻿using System.Linq;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Interceptors;
using K9Nano.Share.Exceptions;

namespace K9Nano.RemoteClient
{
    public class ExceptionInterceptor : Interceptor
    {
        private static void HandleException(RpcException ex)
        {
            var entry = ex.Trailers?.FirstOrDefault(x => x.Key == "code");
            if (entry != null)
            {
                var code = int.Parse(entry.Value);
                var error = ex.Trailers.FirstOrDefault(x => x.Key == "error")?.Value;
                throw new K9UserFriendlyException(code, error);
            }
        }

        public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request,
            ClientInterceptorContext<TRequest, TResponse> context,
            BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
        {
            try
            {
                return continuation(request, context);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Unknown)
            {
                HandleException(ex);

                throw;
            }
        }

        public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
        {
            try
            {
                return continuation(request, context);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Unknown)
            {
                HandleException(ex);

                throw;
            }
        }

        public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
        {
            try
            {
                return continuation(request, context);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Unknown)
            {
                HandleException(ex);

                throw;
            }
        }

        public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
        {
            try
            {
                return continuation(context);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Unknown)
            {
                HandleException(ex);

                throw;
            }
        }

        public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
        {
            try
            {
                return continuation(context);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Unknown)
            {
                HandleException(ex);

                throw;
            }
        }

        public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context,
            UnaryServerMethod<TRequest, TResponse> continuation)
        {
            try
            {
                return await base.UnaryServerHandler(request, context, continuation);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Unknown)
            {
                HandleException(ex);

                throw;
            }
        }

        public override async Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, ServerCallContext context,
            ClientStreamingServerMethod<TRequest, TResponse> continuation)
        {
            try
            {
                return await base.ClientStreamingServerHandler(requestStream, context, continuation);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Unknown)
            {
                HandleException(ex);

                throw;
            }
        }

        public override async Task ServerStreamingServerHandler<TRequest, TResponse>(TRequest request, IServerStreamWriter<TResponse> responseStream,
            ServerCallContext context, ServerStreamingServerMethod<TRequest, TResponse> continuation)
        {
            try
            {
                await base.ServerStreamingServerHandler(request, responseStream, context, continuation);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Unknown)
            {
                HandleException(ex);

                throw;
            }
        }

        public override async Task DuplexStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream,
            IServerStreamWriter<TResponse> responseStream, ServerCallContext context, DuplexStreamingServerMethod<TRequest, TResponse> continuation)
        {
            try
            {
                await base.DuplexStreamingServerHandler(requestStream, responseStream, context, continuation);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.Unknown)
            {
                HandleException(ex);

                throw;
            }
        }
    }
}